#include "ctiic.h"
#include "gt9147.h" // for T_IIC_PORT and pin macros

/*
 * Bit-bang I2C timing helpers
 * These delays are conservative to ensure signal integrity at low speeds.
 * If needed, tune them based on your board and GT9147 requirements.
 */
static void iic_delay(void)
{
  /* ~2-3 us on typical F4 @ 168MHz with -O2; adjust if needed */
  HAL_Delay(1);
}

static void SDA_OUT(void)
{
  GPIO_InitTypeDef gpio = {0};
  gpio.Pin              = T_SDA_PIN;
  gpio.Mode             = GPIO_MODE_OUTPUT_PP;
  gpio.Pull             = GPIO_PULLUP;
  gpio.Speed            = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(T_IIC_PORT, &gpio);
}

static void SDA_IN(void)
{
  GPIO_InitTypeDef gpio = {0};
  gpio.Pin              = T_SDA_PIN;
  gpio.Mode             = GPIO_MODE_INPUT;
  gpio.Pull             = GPIO_PULLUP;
  gpio.Speed            = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(T_IIC_PORT, &gpio);
}

static void SCL_OUT(void)
{
  GPIO_InitTypeDef gpio = {0};
  gpio.Pin              = T_SCK_PIN;
  gpio.Mode             = GPIO_MODE_OUTPUT_PP;
  gpio.Pull             = GPIO_PULLUP;
  gpio.Speed            = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(T_IIC_PORT, &gpio);
}

static void SDA_HIGH(void)
{
  HAL_GPIO_WritePin(T_IIC_PORT, T_SDA_PIN, GPIO_PIN_SET);
}
static void SDA_LOW(void)
{
  HAL_GPIO_WritePin(T_IIC_PORT, T_SDA_PIN, GPIO_PIN_RESET);
}
static void SCL_HIGH(void)
{
  HAL_GPIO_WritePin(T_IIC_PORT, T_SCK_PIN, GPIO_PIN_SET);
}
static void SCL_LOW(void)
{
  HAL_GPIO_WritePin(T_IIC_PORT, T_SCK_PIN, GPIO_PIN_RESET);
}
static GPIO_PinState SDA_READ(void)
{
  return HAL_GPIO_ReadPin(T_IIC_PORT, T_SDA_PIN);
}

void CT_IIC_Init(void)
{
  /* Enable port clock (macro provided in gt9147.h) */
  T_IIC_PORT_ENABLE();

  /* Configure SCL & SDA to open-drain like behavior using push-pull + pull-up simulated */
  SCL_OUT();
  SDA_OUT();

  /* Idle state: both high */
  SCL_HIGH();
  SDA_HIGH();
}

void CT_IIC_Start(void)
{
  SDA_OUT();
  SDA_HIGH();
  SCL_HIGH();
  iic_delay();
  /* START: SDA high -> low while SCL high */
  SDA_LOW();
  iic_delay();
  SCL_LOW();
  iic_delay();
}

void CT_IIC_Stop(void)
{
  SDA_OUT();
  SCL_LOW();
  SDA_LOW();
  iic_delay();
  /* STOP: SDA low -> high while SCL high */
  SCL_HIGH();
  iic_delay();
  SDA_HIGH();
  iic_delay();
}

/* return 0 on ACK received, 1 on NACK timeout */
uint8_t CT_IIC_Wait_Ack(void)
{
  uint16_t timeout = 1000;

  SDA_IN();   /* release SDA */
  SDA_HIGH(); /* ensure pull-up */
  iic_delay();
  SCL_HIGH();
  iic_delay();

  while (SDA_READ() == GPIO_PIN_SET) { /* wait for low (ACK) */
    if (timeout-- == 0) {
      SCL_LOW();
      return 1; /* NACK */
    }
  }

  SCL_LOW();
  iic_delay();
  return 0; /* ACK */
}

static void CT_IIC_Send_Bit(uint8_t bit)
{
  SDA_OUT();
  if (bit)
    SDA_HIGH();
  else
    SDA_LOW();
  iic_delay();
  SCL_HIGH();
  iic_delay();
  SCL_LOW();
}

void CT_IIC_Send_Byte(uint8_t txd)
{
  for (int i = 0; i < 8; i++) {
    CT_IIC_Send_Bit((txd & 0x80) != 0);
    txd <<= 1;
  }
  /* Release SDA for ACK */
  SDA_IN();
  SDA_HIGH();
}

uint8_t CT_IIC_Read_Byte(uint8_t ack)
{
  uint8_t receive = 0;
  SDA_IN();
  for (int i = 0; i < 8; i++) {
    receive <<= 1;
    SCL_HIGH();
    iic_delay();
    if (SDA_READ() == GPIO_PIN_SET) receive |= 0x01;
    SCL_LOW();
    iic_delay();
  }
  /* Send ACK/NACK */
  SDA_OUT();
  if (ack)
    SDA_LOW();
  else
    SDA_HIGH();
  iic_delay();
  SCL_HIGH();
  iic_delay();
  SCL_LOW();
  SDA_IN(); /* release */
  return receive;
}
